package cn.fang2chen.java.distributed.lock.framework;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;

/**
 * @author dcmini chen
 * @since 1.0.0
 */
public abstract class AbstractDistributedQueuedSynchronizer extends AbstractQueuedSynchronizer {

    /**
     * 获取远程锁同步器
     *
     * @return 远程锁同步器
     */
    protected abstract RemoteLockSynchronizer remoteLockSynchronizer();

    /**
     * 阻塞式 请求分布式锁
     * 1、请求获取本地所
     * 2、请求获取远程锁
     *
     * @param arg 参数值
     */
    public void acquireDistributed(int arg, RemoteLockParam remoteLockParam) {
        acquire(arg); // 请求本地锁
        remoteLockAcquire(remoteLockParam);  // 请求远程锁
    }

    /**
     * 获取远程锁
     */
    protected void remoteLockAcquire(RemoteLockParam remoteLockParam) {
        remoteLockSynchronizer().acquire(remoteLockParam);
    }

    /**
     * 尝试 请求分布式锁
     * 1、尝试 获取 本地所
     * 2、尝试获取远程锁
     * 3、如果需要则释放本地所
     *
     * @param arg 参数值
     */
    public boolean tryAcquireDistributed(int arg, RemoteLockParam remoteLockParam) {
        if (!tryAcquire(arg)) {  // 尝试获取本地锁
            return false;
        }

        if (!remoteLockSynchronizer().tryAcquire(remoteLockParam)) {  // 尝试获取远程锁
            tryRelease(arg);  // 没有获取到远程锁 需要释放本地锁
            return false;
        }

        return true;
    }

    /**
     * 指定时间内 尝试 获取分布式锁
     *
     * @param arg 参数值
     * @param nanosTimeout 尝试的时间
     */
    public boolean tryAcquireNanosDistributed(int arg, long nanosTimeout, RemoteLockParam  remoteLockParam) throws InterruptedException {

        long deadline = System.nanoTime() + nanosTimeout;

        if (!tryAcquireNanos(arg, nanosTimeout)) {
            return false;
        } else { // 拿到了本地锁
            long remaining = deadline - System.nanoTime();
            if (remaining > 0 && remoteLockSynchronizer().tryAcquire(remoteLockParam, remaining, TimeUnit.NANOSECONDS)) {
                return true;
            } else {
                release(arg);
                return false;
            }
        }
    }

    /**
     * 可以被中止 获取分布式锁
     *
     * @param arg 参数值
     * @throws InterruptedException if the current thread is interrupted
     */
    public void acquireInterruptiblyDistributed(int arg, RemoteLockParam  remoteLockParam) throws InterruptedException {
        acquireInterruptibly(arg);
        try {
            remoteLockSynchronizer().acquireInterruptibly(remoteLockParam);
        } catch (InterruptedException e) {
            release(arg);  // 到这里需要释放本地锁
            throw e;
        }

    }

    /**
     * 释放分布式锁
     * 先释放本地锁，当前进程可以一起进入远程锁竞争
     * 然后再释放远程锁
     *
     * @param arg 参数值
     */
    public boolean releaseDistributed(int arg, RemoteLockParam remoteLockParam) {
        return release(arg) && remoteLockRelease(remoteLockParam);
    }

    /**
     * 远程锁释放
     */
    protected boolean remoteLockRelease(RemoteLockParam remoteLockParam) {
        return remoteLockSynchronizer().release(remoteLockParam);
    }

    public boolean isLocked(RemoteLockParam remoteLockParam) {
        return remoteLockSynchronizer().isLocked(remoteLockParam);
    }

    public long remainingTime(RemoteLockParam remoteLockParam) {
        return remoteLockSynchronizer().remainingTime(remoteLockParam);
    }


}
